import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="For Developers/Token Implementation Notes" />

Tokens are placeholders for specific values in our CSS. For example, instead of specifiying text color as a dark gray, we can simply say it's the "text color" and let each theme fill in the details.

Tokens are what power Radial's themability. By building our components with tokens instead of specific values, we allow them to be easily rethemed. Tokens won't cover everything we want in a new design but they let us tune the defaults in a way that resembles configuration more than coding.

## Glossary

There are a few terms around tokens that can be confusing so let's define them up front.

**Variables:** A name and a value. Here we're usually talking about CSS custom properties, also known as CSS variables. This is the technical feature we use to implement tokens.

**Tokens:** CSS Variables that a developer uses when implementing the base styles for a component. Not all variables are tokens. For example, a theme might have a local palette of colors that's used to set the token values. When I write some code for a theme that defines _button color_ as _brand red_, _button color_ is a token, but _brand red_ isn't.

**Dials:** Variables that can be set by a theme author to change the value of a single token. For example, _button color_.

**Levers:** Variables that can be set by a theme author to change the value of multiple tokens. For example, the font scaling factor which changes all of the derived font size tokens.

## CSS Custom Properties

We chose to go with CSS custom properties over SASS variables because of several features that weren't easily replicated with SASS.

Advantages
- Preserving the variable names in source allows us to use the CSS as a source of truth in our storybook docs.
- Being able to see the variable names in the browser devtools makes debugging more transparent.
- CSS's scoping rules make implementing color groups using CSS variables pretty straightforward.

Disadvantages:
- They have limited support in IE.

## Token Linting

We use stylelint to enforce an allowlist so that things like theme specific colors don't get added to component styles.

Right now we only apply this check to the base styles and are less strict with what goes into the site specific themes themselves. There's no benifit to saying _brand theme_ can't use _brand color_ directly.

## Component Specific Tokens

TODO: Add recommendations for adding dials to components. This will probably be detailed more in its own doc or a doc about components.

## Typography

### Typefaces

Our set of typography tokens supports up to four typefaces. A header, subheader, body, and small font. In addition to tokens to set the font family, we have tokens to set the default values for font weight and letter spacing. This gives a theme author the option to use a custom default weight for headers, such as a semibold (600) or ultrabold (900) header, or a custom default line spacing.

Tokens for a typeface:
```
--font-family-header:    'Libre Baskerville', 'Times New Roman', serif;
--letter-spacing-header: normal;
--font-weight-header:    700;
```

### Font Scale

For font sizes we use a modular font scale. A theme author only needs to set the levers, `--font-base` (a font size in pixels) and `--font-ratio` to adjust the modular font scale. This will configure a range of font size tokens numbered from 1 to 16 with `--font-size-6` matching the base size. Size 1 is the smallest, and size 16 is the largest.

There are recommendations for designers to try to restrict certain typefaces to certain ranges of font sizes for consistency.

### Line Heights

There's also a set of default line-height tokens to match each font size token. Because ideal default line heights for fonts can vary depending on the typeface and context, these need to be configured manually.

When a developer uses a `--font-size-*` token they should generally use the correspending `--line-height-*` token, unless manually setting a different height makes sense (for example, single-line text that should be vertically centered, such as in a button or tag, might have a custom line height to match the container size.)

### The typeface mixin
To help developers always include the font-weight, letter-spacing, and line-height tokens when setting a font, the typeface mixin sets all these values in a single line.

```
@include typeface(header, 10)
```
will expand to
```
font-family:    var(--font-family-header);
letter-spacing: var(--letter-spacing-header);
font-weight:    var(--font-weight-header);
font-size:      var(--font-size-10);
line-height:    var(--line-height-10);
```

A recommended pattern is to use the typeface mixin whenever setting a typeface for a component, element or section, but then use only the font size and line height tokens in media queries or modifier classes that adjust the size.

Example font token usage:
```
.my-header {
  // the base font size
  @include typeface (header, 8);

    @include media(">=medium") {
      // increase the font size on a wider screen
      font-size:      var(--font-size-10);
      line-height:    var(--line-height-10);
    }
}

.my-header.mod-smaller {
    font-size:      var(--font-size-6);
    line-height:    var(--line-height-6);

    @include media(">=medium") {
       font-size:      var(--font-size-8);
       line-height:    var(--line-height-8);
    }
}
```

```
// DON'T DO THIS
.my-button {
  @include typeface (body, 6);
  line-height: 100%; // customized line height for buttons.
}

.my-button.mod-small {
  @include typeface (body, 4); // overwrites the customized line height. oops!
}
```

## Spacing

Radial uses a range of spacing tokens, numbered `--space-1` to `--space-11`. These tokens are used for margin and padding values within and between components. Our goal with spacing tokens is to be able to choose the visual density for a theme.

When creating components, developers and designers should use these tokens consistely according to the [spacing guidelines](./index.html?path=/docs/foundations-spacing--page#spacing-usage).

When creating a theme, these values aren't configured manually. Radial includes three predefined spacing scales, Compact, Default, and Loose. To choose the spacing scale for a theme, the theme author can change the variables `--spacing-base` and `spacing-increment` to use a pair of these predefined variables.

Example: Setting a theme to use compact spacing
```
  --spacing-base:      var(--spacing-base-compact);
  --spacing-increment: var(--spacing-increment-compact);
```

Setting the above levers will automatically configure the values of `--space-1` to `--space-11`.

## Colors

### Color Format

One nice feature of SASS is its built in color functions. You can write `rgba(#ff000, 0.8)` and it will become `rgba(255, 0, 0, 0.8)`. Or more usefully you might write something like `rgba($theme-red, $hover-opacity)`.

Unfortunately things aren't so simple in pure CSS.

The problem:
We want to be able to store colors in a way where:

1. We can apply opacity to the colors.
2. The variable names are preserved as CSS variables even after compilation.

Obstacles:
- SASS variables wont work because the variable names get compiled out.
- Supported CSS color formats won't let use rgb colors:
- Using the 8 digit hex format (#RRGGBBAA) almost works, but we can't concatenate css vars without a space in-between. `--my-green: '#229922'; --my-opacity: 'cc'; color: var(--my-green)var(--my-opacity);` doesn't work. nor does `--my-green: '#229922'; color: var(--my-green)cc;`.
-  `--my-green: '#229922'; rgba(#229922, 0.8)` also won't work in plain CSS.

We ended up storing colors as a comma separated set of rgb values: e.g. `--my-green: "34,153,34";`
This lets us do `color: rgba(var(--my-green), 0.8)` or even `color: rgba(var(--my-green), (--opacity-token))`

Since the design team uses hex colors in their designs, we include a sass function that will convert hex colors to these rgb values, to make theme files easier to parse.

`--my-green: hex2rgb("229922");` becomes `--my-green: "34,153,34"` in the compiled css.

I didn't find this until after we sorted this all out, but the first part of this blog post covers this issue and came to basically this exact same solution: https://blog.jim-nielsen.com/2019/generating-shades-of-color-using-css-variables/

### The base palette

Because color tokens will need to be customized for each theme and documented in several places, we don't want to go overboard with the list of color tokens. The base set should include colors that are used widely in multiple places across our sites and provide a useful default palette that we can use to build our components.

#### Primary Theme Colors

The guidelines for where to use these within components are still a little unclear, but basically these are the brand colors for the site.

#### Background Colors

The main background color and some alternates for special classes.

You can create mod classes or overrides to use these alternative background colors.

```
//theme.overrides.components.popup.scss
.popup.mod-muted {
  // this theme provides an alternative style for popups with a less bright background color
  --color-background: var(--color-background-muted);
}

//theme.overrides.components.footer.scss
.footer {
  // this theme has uses a less bright background color for its footer
  --color-background: var(--color-background-muted);
}
```

#### Text Colors

Colors for various types and states of text. Something to note here is that the definition of "text" is pretty flexible. Text color tokens can be used as defaults for any "text-like" things, such as inline icons, list bullets, etc. Basically anything that sits inline and needs to have contrast with the background.

### Link Colors

A typical site will probably customize links in multiple places, but as defaults to use for building our components, we provide two sets of link tokens. The defaults for links, `--color-link`, `text-decoration-link`, ` --color-link-hover`, ` --text-decoration-link-hover`, `--opacity-link-hover` and a second set to use in navigation elements, `--color-link-navigation`, etc.

### Tag Colors

Customize the default appearance of "tag" atoms that are used in various components.

### Button Colors

Customize the default appearance of buttons. Right now we assume light text on a dark background. `--color-button` is the `background-color` of the button.

### Color Groups

We use "color groups" to allow for multiple color schemes without adding new tokens. Typically there are two color groups in a theme, a "light" group and a "dark" group.

A color group is a new set of values for the same tokens. The dark group might have dark background colors and light text colors.

You can look at the colors file in the white label theme for an example.

Color groups are usually implemented as a SASS mixin that can apply the new values. You can apply this mixin to the element you want to use the color group in your overrides.

```
// my-theme/overrides/components/_header.scss
.header {
  // this theme has a header with a dark background
  @include color-group-dark;
}
```
